package com.towexin.config;

import com.alibaba.fastjson.JSON;
import com.towexin.common.ResultData;
import com.towexin.application.system.domain.ExceptionLog;
import com.towexin.application.system.service.ExceptionLogService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * TODO
 * 配合shiro权限认证失败页面内容设置
 * 适用于使用注解配置权限拦截
 *
 * @author Towexin
 * @version 1.0
 * @date 2021/4/14 14:27
 */

@RestControllerAdvice
public class PublicExceptionHandler {
    private static final Logger log = LogManager.getLogger(PublicExceptionHandler.class);

    @Autowired
    private ExceptionLogService service;

    /**
     * 捕获权限不足异常
     *
     * @param exception
     * @param request
     * @param response
     */
    @ExceptionHandler({UnauthorizedException.class})
    public void processUnauthenticatedException(UnauthorizedException exception, ServletRequest request, ServletResponse response) {
        log.info("****** 权限异常 ******, message: {}", exception.getMessage());
        exception.printStackTrace();
        try {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            // 获取响应头中ajax
            String header = req.getHeader("X-Requested-With");
            // 如果是ajax请求
            if ("XMLHttpRequest".equals(header)) {
                // 设置响应头
                resp.setContentType("application/json;charset=UTF-8");
                resp.getWriter().write(JSON.toJSONString(new ResultData(401, "身份认证失败")));
            } else {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("text/html");
                PrintWriter out = response.getWriter();

                // 1. 创建模板解析器 并设置相关属性
                ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
                // 配置解析器模板存储文件夹路径
                resolver.setPrefix("template-model/");
                // 配置解析器模板文件后缀
                resolver.setSuffix(".html");

                // 通过template模板获取html内容
                TemplateEngine templateEngine = new TemplateEngine();
                templateEngine.setTemplateResolver(resolver);

                // 更新请求的页面的内容为无权限页面
                out.write(templateEngine.process("no-auth", new Context()));
                out.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 捕获 UnknownAccountException 异常，判断是否在登录中被冻结
     *
     * @param exception
     * @param request
     * @param response
     */
    @ExceptionHandler({UnknownAccountException.class})
    public void userLoginedAndIsLock(UnknownAccountException exception, ServletRequest request, ServletResponse response) {
        log.info("UnknownAccountException 异常，message: {}", exception.getMessage());
        exception.printStackTrace();
        if (exception.getMessage().equals("userLoginLock")) {
            try {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("text/html");
                PrintWriter out = response.getWriter();

                // 1. 创建模板解析器 并设置相关属性
                ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
                // 配置解析器模板存储文件夹路径
                resolver.setPrefix("template-model/");
                // 配置解析器模板文件后缀
                resolver.setSuffix(".html");

                TemplateEngine templateEngine = new TemplateEngine();
                templateEngine.setTemplateResolver(resolver);

                Context context = new Context();
                context.setVariable("tipText", "当前用户已被冻结，请联系管理员处理");
                context.setVariable("title", "账户已冻结");

                out.write(templateEngine.process("tip-login", context));

                SecurityUtils.getSubject().logout();
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 捕获全局异常，记录到数据库
     *
     * @param exception
     */
    @ExceptionHandler({Exception.class})
    public void processUnauthenticatedException(Exception exception) {
        log.info("****** 捕获全局异常 ******");
        exception.printStackTrace();
        log.info("****** 捕获全局异常结束 ******");
        List<ExceptionLog> exceptionLogs = new ArrayList<>();
        for (int i = 0; i < exception.getStackTrace().length; i++) {
            ExceptionLog exceptionLog = new ExceptionLog();
            exceptionLog.setMessage(exception.getMessage());
            exceptionLog.setDeclaringClass(exception.getStackTrace()[i].getClassName());
            exceptionLog.setMethodName(exception.getStackTrace()[i].getMethodName());
            exceptionLog.setLineNumber(exception.getStackTrace()[i].getLineNumber());
            exceptionLog.setType(exception.getClass().getName());
            exceptionLogs.add(exceptionLog);
        }
        service.saveBatch(exceptionLogs);
    }

}
